
local old_type = type
function type(v)
	local t = old_type(v)
	return (t=="table" and v.__type) or t
end

function check(v, t, n, f, e)
	e = e or 2
	if type(v) ~= t then
		error((f and (f..": ") or "")..(n and ("argument #"..n..": ") or "").."expected "..t..", got "..type(v), e+1)
	end
end
function checkopt(v, t, n, f, e)
	e = e or 2
	if type(v) ~= "nil" then check(v, t, n, f, e+1) end
end

function class(name, def)
	if type(name) ~= "string" then error("class: argument #1: expected name string, got "..type(name), 2) end
	if type(def) ~= "table" then error("class: argument #2: expected definition table, got "..type(def), 2) end
	
	if def.__type ~= nil then error("class "..name.." definition: __type field: expected nil, got "..type(def.__type), 2) end
	
	if def.__index ~= nil then
		if type(def.__index) ~= "function" then error("class "..name.." definition: __index field: expected nil or function, got "..type(def.__index), 2) end
		local old_index = def.__index
		def.__index = function(t, k) return old_index(t, k) or getmetatable(t)[k] end
	else
		def.__index = function(t, k) return rawget(t, k) or getmetatable(t)[k] end
	end
	
	def.__type = name
	return function(...)
		local input
		if def.__new then
			input = def.__new(...)
			if type(input) ~= "table" then error("class "..name.." custom constructor: return value: expected new object (table), got "..type(input), 2) end
		else
			local argt = {...}
			if #argt > 1 then error("class "..name.." constructor: too many arguments: expected 0 or 1 arguments, got "..#argt, 2) end
			
			input = argt[1]
			if input == nil then input = {}
			elseif type(input) ~= "table" then error("class "..name.." constructor: argument #1: expected nil or table, got "..type(input), 2) end
		end
		
		setmetatable(input, def)
		
		return input
	end
end
